<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
        <script src="/lib/axios.js"></script>
        <script src="/lib/vue.js"></script>
        <link rel="stylesheet" href="/lib/elementindex.css" />
        <script src="/lib/elementindex.js"></script>
        <script src="/lib/jsplumb.js"></script>
        <link rel="stylesheet" href="/lib/jsplumbtoolkit-defaults.css" />
        <style>
            .flownode{
				font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif;
				font-size: 14px;
				color: #606266;
				text-align: center;
				
				opacity:0.9;
				border: 2px solid transparent;
				border-radius: 8px;
                  -webkit-touch-callout: none;
                  -webkit-user-select: none;
                  -khtml-user-select: none;
                  -moz-user-select: none;
                  -ms-user-select: none;
                  user-select: none;  
                
				min-width: 80px;
				width:auto;
				max-width: 160px;
				line-height: 20px;/*override the aside component value*/
				
				/*图片居中*/
				/* background-image: url(/img/start32.png) ; */
				background-position: top;
				background-repeat:no-repeat;
				
				/*定位文本，空出背景图片*/
				padding-top: 35px;
                padding-left: 4px;
                padding-right: 4px;
                
				
				/*禁止换行*/
				white-space: nowrap;
				overflow: hidden;
				text-overflow: ellipsis;
			}
			.start{
				min-width: 46px;
				background-image: url(/img/start32.png) ;
			}
			.task{
				background-image: url(/img/task32.png) ;
			}
			.switch{
				background-image: url(/img/switch32.png) ;
			}
			.merge{
				background-image: url(/img/merge32.png) ;
			}
			.end{
				min-width: 46px;
				background-image: url(/img/end32.png) ;
			}
			.flownode:hover{
				color: #303133;
				opacity:1.0;
				border: 2px dotted lightgreen;
				cursor: pointer; 
			}
			/*used by code*/
			.flownode-hover{
				color: #303133;
				opacity:1.0;
				border: 2px dotted lightgreen;
				cursor: pointer;
			}
			.flownode-focused{
				color: #303133;
				background-color: #e0f6dc;
				border: 2px solid lightgreen;
			}
			
			.linelabel
			{
				position: absolute;
				
				font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif;
				color: #606266;
				font-size: x-small;
	
				opacity:0.8;
				min-height: 12px;
				min-width: 20px;
				padding: 4px;
				user-select: none;
				
				border-radius: 4px;
				
				background-image: url(/img/drag-drop-line16.png) ; 
				background-position: left;
				background-repeat:no-repeat;
				
				/*定位文本，空出背景图片*/
				padding-left: 18px;
				
				/*禁止换行*/
				white-space: nowrap;
				overflow: hidden;
				text-overflow: ellipsis;
			}
			
			.linelabel:hover
			{
				color: #303133;
				border: lightgreen dotted;
				opacity:1.0;
			}
			
			.linelabel-hover/*working with .linelabel*/
			{
				color: #303133;
				border: green solid;
				opacity:1.0;
			}
            .el-input-group__prepend 
            {
                padding: 0px 6px;
            }
            .el-input-group__append
            {
                padding: 0px;
            }
            
		</style>

        <style>
            .el-dropdown-link {
            cursor: pointer;
            color: #409EFF;
          }
          .el-icon-arrow-down {
            font-size: 12px;
          }
          
          .jtk-drag-hover {
             /*when dray hover on element */
              border: 2px solid lightgreen;
          }
          
          .jtk-endpoint-drop-allowed  {
              border: 1px solid transparent; 
              background-color:skyblue;
          }

          .jtk-endpoint-drop-forbidden
          {
              border: 2px solid transparent;
              background-color:transparent;
          }
         
          .el-collapse-item__header {
              background-color: #B3C0D1;
          }
          .el-collapse-item__content {
              background-color: #B3C0D1;
          }
          .flownode-panel-title {
              font-size:larger;
              font-weight: bold;
              padding-left: 10px;
          }
           
        .el-dialog__body{
            padding-top: 0px;
        }
        
        .el-table td
        {
            padding-top: 2px;
            padding-bottom: 2px;
        }
        </style>
    </head>

    <body style="">
        <div id='app' style="width:100%;">
            <section style="display: flex;">
                <div style='position:fixed;z-index:100; 
                opacity: 0.7;background-color: #B3C0D1;
                height:48px;width:100%; padding-left:4px; padding-top: 4px;text-align: left;display: flex; '>
                   <span style="width:240px;padding-left: 10px;padding-top: 2px;">
<!--                        <el-input v-model='ruleDef.rname' style="width:300px">
                            <template slot="prepend">名称:</template>
                        </el-input>
                        <el-input :value="ruleDef.rpath" disabled style="width:240px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;">
                            <template slot="prepend">代码:</template>
                        </el-input> -->
                        <el-button size='medium' @click='handleRetsSetting' type="primary">流程设置</el-button>
                        <el-button size='medium' :disabled="!/^task$|^switch$/.test(present.itemType)" @click='handleNodeDblClick' type="primary">节点设置</el-button>
                    </span>

                    <span style="padding-left: 10px;padding-top: 2px;text-align: right;right:10px">
                        <component v-bind:is="layoutTool.control" v-bind:layouttool='layoutTool'></component>
                        <div style='width: 10px; display: inline-block;'> </div>
                        <!-- <el-button size='medium' @click='showRmemo=true' type="primary">备注</el-button> -->
                        
                        <el-button size='medium' @click='showRtest=true' type="primary">TEST</el-button>
                        <el-button size='medium' type="primary" @click='handleUpdateRuleEvent'>保存</el-button>
                        <el-button size='medium' plain type="warning" @click='handleDeleteRuleEvent'>删除</el-button>
                    </span>
                </div>

                <el-collapse :value="['rulenode', 'flownode']" style="position:fixed;z-index:99;background-color: #B3C0D1;opacity: 0.7;
                                     width:200px;min-height: 400px;margin-top: 54px;padding-top: 10px; text-align:center;">
                    <el-collapse-item name='flownode'>
                        <template slot="title">
                            <span class='flownode-panel-title'>流程组件</span>
                        </template>
                        <div style="background-color:inherit;">
                            <div style="display: flex;text-align:center;">
                                <div class='flownode task' nodetype='task' draggable='true' ondragstart='handleDragStart(event)'>
                                    任务
                                </div>
                                <div style="height: 10px;"></div>
                                <div class='flownode switch' nodetype='switch' draggable='true' ondragstart='handleDragStart(event)'>
                                    分支
                                </div>
                                <div style="height: 10px;"></div>
                            </div>
                        </div>
                    </el-collapse-item>
                    <el-collapse-item name='rulenode'>
                        <template slot="title">
                            <span class='flownode-panel-title'>决策组件</span>
                        </template>
                        <hc-flow-rulepanel></hc-flow-rulepanel>
                    </el-collapse-item>
                </el-collapse>

                <main id='diagramContainer' style="flex-basis: auto;flex:1; min-height: 800px;">
                    <div :class='item$type$map[item.itemType].css' :style="'position:absolute;top:'+item.positionTop + 'px;left:'+item.positionLeft+'px'"
                        v-for="item in ruleDef.nodeList" :key='item.id' :id="viewId(item.id)" @click='handleNodeClick(item)'
                        :ondblclick="'handleNodeDblClick('+item.id+')'">
                        {{item.referName}}
                    </div>
                </main>
            </section>

            <el-dialog :visible.sync="showRmemo" title="规则备注">
                <el-input type="textarea" v-model="ruleDef.rmemo" placeholder="决策组件备注说明信息" rows="12">
                </el-input>
                <el-button @click='showRmemo=false' type="primary">确认</el-button>
                <el-button @click='showRmemo=false'>取消</el-button>
            </el-dialog>
            <el-dialog :visible.sync="showRtest" title="流程测试">
                <hc-test :rpath="ruleDef.rpath" :inputs='ruleDef.rtest' @save-inputs="ruleDef.rtest = $event"></hc-test>
                <el-button @click='showRtest=false' type="primary">确认</el-button>
                <el-button @click='showRtest=false'>取消</el-button>
            </el-dialog>
        </div>

        <style>
            #selectbox{
          position:absolute;
          border:2px dashed darkblue;
          background:#5a72f8;
          width:0;
          height:0;
          z-index:1000;
          filter:alpha(opacity:10);
          opacity:0.1;
          display: none;
      }
      .flownode-selected
      {
          color: #303133;
          opacity:1.0;
          border: 2px dashed lightgreen;
          cursor: pointer; 
      }
  </style>
        <div id='selectbox'></div>
        <link rel="stylesheet" href="/rule/flow/hc-flow-rulepanel.css">
        
        <script src="/lib/ace-noconflict/ace.js"></script>
        <link rel="stylesheet" href="/com/hc-lang-editor.css"/>
        <script type="module">
            import * as $util from '/lib/hc-util.js'
            window.$util = $util;
            import * as $rest from '/lib/hc-rest-service.js'
            import {} from '/com/hc-test.js'
            import {} from '/com/hc-rule-basic-properties.js'
            import {$fu,item$type$map,hc$flowitem$type$map,hc$flowitem$type$list} from './hc-flow-util.js'
            import {} from '/rule/flow/hc-flow-rulepanel.js'
            import FlowLayoutModule from '/rule/flow/hc-flow-layout.js'
            import PropertyConfig from '/rule/flow/hc-flow-properties.js'
            // import ReturnsSetting from '/rule/flow/hc-returns-setting.js'
            import FlowRuleSetting from '/rule/flow/hc-flow-setting.js'
            
            var layoutTool = new FlowLayoutModule({
                layoutUpdated: function(list) {
                    jsPlumb.revalidate(list);
                },
                containerClicked:function()
                {   
                   vm.present = {};
                },
            });
       
            function handleConnection(evt, original) {
                evt.connection.addOverlay(["Custom", {
                    create: function(connection) {
                        // console.log('evt.addOverlay(["Custom"', connection);
                        let labelid = $fu.lineLabelId(connection.getData());
                        let labelname = connection.getData().referName || '';
                        let label = document.getElementById(labelid)
                        if (!label) {
                            let labelText =
                                `
                                <span class='linelabel'
                                id="${labelid}" 
                                ondrop='handleLineLabelDrop(event)'
                                 ondragover='handleLineLabelDragOver(event)' 
                                 ondragenter='handleLineLabelDragEnter(event)'
                                 ondragleave='handleLineLabelDragLeave(event)'>
                                 ${labelname}
                                </span>
                                `
                            label = document.createElement('span')
                            document.body.appendChild(label)
                            label.innerHTML = labelText;
                            label = document.getElementById(labelid);
                        }
                        return label;
                    },
                    location: 0.5,
                    id: "customOverlay"
                }]);

            };

            function handleConnectionDetached(evt, original) {
                // console.log("handleConnectionDetached", evt, original)
                function removeLine(model) {
                    if (vm.present.id == model.id) vm.present = $fu.nopresent;

                    let index = vm.ruleDef.lineList.findIndex(it => it.id == model.id);

                    if (index >= 0) vm.ruleDef.lineList.splice(index, 1);
                    else console.warn('Line does not exists in vm.ruleDef.lineList.', model);
                };

                removeLine(evt.connection.getData())
            };

           function removeLineInView(line) {
                line.deletedByCode = true;
                
                let selected = jsPlumb.getConnections({
                    scope: '*',
                    source: $fu.viewId(line.sourceId),
                    target: $fu.viewId(line.targetId),
                }, true);

                let finded = selected.find(it => it.getData().id == line.id)

                //model updated automatically in detached event handler;
                if (finded) {
                    jsPlumb.deleteConnection(finded);
                } else console.warn('removeLineInView,line is not founded:', line)
            };

            function handleBeforeDetach(con)
            { 
              return con.getData().deletedByCode?true:false;
            };

            function drawLine(it) {
                var connectDefaults = {
                    connector: 'StateMachine', //StateMachine Flowchart 
                    paintStyle: {
                        stroke: 'lightblue',
                        outlineStroke: 'transparent',
                        outlineWidth: 1,
                        strokeWidth: 2
                    },
                    hoverPaintStyle: {
                        outlineWidth: 2,
                        strokeWidth: 3,
                    },
                    endpointStyle: {
                        fill: 'lightblue',
                        radius: 2, //4,
                        outlineWidth: 1
                    },
                    endpointHoverStyle: {
                        fill: 'skyblue',
                    },
                    overlays:[
                        ['Arrow', {
                            width: 10,
                            length: 10,
                            location: 1
                        }],
                   ],
                  anchors: ['Continuous', 'Continuous'],
                };
                
                var line = jsPlumb.connect({
                    source: $fu.viewId(it.sourceId),
                    target: $fu.viewId(it.targetId),
                    data: it,
                }, connectDefaults)
            };

            // var vm;
            var vmcfg = {
                el: '#app',
                data: {
                    ruleDef: {},
                    present: $fu.nopresent,
                    item$type$map: item$type$map,
                    showRtest: false,
                    showRmemo: false,
                    layoutTool: layoutTool,
                },
                methods: {
                    handleNodeDblClick(){handleNodeDblClick()},
                    viewId: function(id) {
                        return $fu.viewId(id)
                    },
                    lineLabelId: function(lineModel) {
                        return $fu.lineLabelId(lineModel);
                    },
                    handleNodeClick: function(node) {
                        this.present = node
                    },
                    handleRetsSetting:function(){
                      // vmrets.show(this.ruleDef.retList);  
                      vmsetting.show(this.ruleDef);
                    },
                    handleUpdateRuleEvent: function() {
                        this.ruleDef.nodeList.forEach(it => {
                            let pos = $fu.getPosition($fu.viewId(it.id))
                            it.positionLeft = pos.left;
                            it.positionTop = pos.top
                        });
                        $rest.updateRuleDefInVm(this)
                    },
                    handleDeleteRuleEvent: function() {
                        $rest.deleteRuleDefInVm(this)
                    }
                },
                watch: {
                    present: function(newVal, oldVal) {
                        if (newVal && newVal.id) {
                            $util.addClass($fu.isFlowNode(newVal) ? $fu.viewId(newVal.id) : $fu.lineLabelId(newVal),
                                'flownode-focused')
                        }
                        if (oldVal && oldVal.id) {
                            $util.removeClass($fu.isFlowNode(oldVal) ? $fu.viewId(oldVal.id) : $fu.lineLabelId(oldVal),
                                'flownode-focused')
                        }
                    },
                },
            }; //end of vm config

            $rest.check$pinfoOrLoad(initvm, 'xsxdjc/Main');
            
            function initvm() {
                window.vm = new Vue(vmcfg);
                $rest.obtainRuleDefByRpath($util.getUrlArgOrDefault('rpath','xsxdjc/FlowTest'), function(ruleDef){
                    vm.ruleDef = ruleDef;
                    //防止vue没有初始化完成视图
                    vm.$nextTick(function() {
                        jsPlumb.ready(function() {
                            jsPlumb.setContainer('diagramContainer')
                            jsPlumb.bind('connection', handleConnection);
                            jsPlumb.bind('beforeDetach',handleBeforeDetach)
                            jsPlumb.bind('connectionDetached', handleConnectionDetached);
                            vm.ruleDef.lineList.forEach(drawLine)
                        }); //end of jsplumn ready  
                    });
                });
                // window.vmrets = new ReturnsSetting();
                window.vmsetting = new FlowRuleSetting();
                window.vmprop = new PropertyConfig(
                {
                    actionUpdate:function(self)
                    {
                        if(self.setting.itemType === 'switch' && vm.present.referId != self.setting.referId)
                        {
                            let targetId = nextNodeId(self.setting.id)
                            if(!targetId)
                             {
                                 self.$message({type:'warning',message:'所有分支指向同一目标节点时，才能删除分支节点 或 修改分支分类，请调整流程之后再试！'});
                                 return false;
                             }
                             
                            removeAllLineInView(self.setting.id,targetId);
                                 
                           let lineList =  createNewLines(self.setting, targetId);
                           
                           vm.$nextTick(function() {
                               lineList.forEach(it=>drawLine(it));
                           });
                        }
                        
                        if(vm.present) Object.assign(vm.present,self.setting);
                        return true;
                    },
                    actionDelete:function(self)
                    {
                        if(!nextNodeId(self.setting.id)) 
                        {
                            self.$message({type:'warning',message:'所有分支指向同一目标节点时，才能删除分支节点 或 修改分支分类，请调整流程之后再试！'});
                            
                            return false;
                        }
                        
                        removeNodeInView(self.setting.id);
                        
                        return true;
                        
                        function removeNodeInView(nodeid) {
                            //step 0. unfocus
                            if (vm.present.id == nodeid)
                                vm.present = $fu.nopresent;
                        
                            //step 1. remove it from model
                            let index = vm.ruleDef.nodeList.findIndex(it => it.id == nodeid)
                            vm.ruleDef.nodeList.splice(index, 1);
                        
                            //step2. Remove the old lines and node
                            let enterList = vm.ruleDef.lineList.filter(it => it.targetId == nodeid);
                            let exitList = vm.ruleDef.lineList.filter(it => it.sourceId == nodeid);
                            enterList.forEach(it=>it.deletedByCode=true);
                            exitList.forEach(it=>it.deletedByCode=true);
                            
                            jsPlumb.remove($fu.viewId(nodeid))
                            
                            //step2. draw the new lines on view
                            vm.$nextTick(function(){
                                enterList.forEach(it=>{
                                    delete it.deletedByCode;
                                    it.targetId = exitList[0].targetId;
                                    vm.ruleDef.lineList.push(it);
                                    drawLine(it);
                                });
                            });
                        };//end of removeNodeInView

                    },
                });
            };
            
            function nextNodeId(nodeId)
            {
                let currentLineList = vm.ruleDef.lineList.filter(it => it.sourceId === nodeId);
                let targetNodeSet = new Set(currentLineList.map(it=>it.targetId));
                return targetNodeSet.size == 1 ? currentLineList[0].targetId : 0;
            }
            
            function removeAllLineInView(sourceId, targetId)
            {
                let oldLines = jsPlumb.getConnections({
                         scope: '*',
                         source: $fu.viewId(sourceId),
                         target: $fu.viewId(targetId),
                     }, true);
                //model updated automatically in detached event handler;
                oldLines.forEach(it=>{
                    it.getData().deletedByCode = true;
                    jsPlumb.deleteConnection(it);
                 }
                );
                return oldLines;
            };
            
            window.handleNodeDblClick = function(nodeid)
            {
                if(/^task$|^switch$/.test(vm.present.itemType))
                    vmprop.show(vm.present);
            }
            
            window.handleDragStart= function(event) {
                event.dataTransfer.effectAllowed = 'copy' //cursor indicator
                
                let nodetype = event.target.getAttribute('nodetype')
                let ruleId = event.target.getAttribute('ruleid')
                let ruleType = event.target.getAttribute('ruletype')
                let data = {
                    itemType: event.target.getAttribute('nodetype'),
                    itemName: event.target.innerText,
                    ruleId:   ruleId,//ruleId ? parseInt(ruleId) : 0,
                    ruleType: ruleType ? ruleType : '',
                    offsetX: event.offsetX,
                    offsetY: event.offsetY,
                };

                event.dataTransfer.setData('application/x-flownode-' + nodetype, JSON.stringify(data));
            }

            window.handleLineLabelDragEnter = function (event) {
                  $util.addClass(event.srcElement, 'linelabel-hover')
            }

            window.handleLineLabelDragLeave = function (event) {
                $util.removeClass(event.srcElement, 'linelabel-hover')
            }

            window.handleLineLabelDragOver = function (event) {
                let dataType = event.dataTransfer.types[0];
                // the application customized appliction/x-... type is not allowed by default
                // need to forbit the default to enable drag-drop
                if(dataType === 'application/x-flownode-task' || dataType === 'application/x-flownode-switch')
                 {
                     event.preventDefault();
                 };
            };

            window.handleLineLabelDrop = function (event) {
                console.log('handleLineLabelDrop', event)

                let dataType = event.dataTransfer.types[0];
                let data = JSON.parse(event.dataTransfer.getData(dataType));
                event.stopPropagation()
                event.preventDefault()

                let newNode = {
                        id: ++ vm.ruleDef.rmaxid,
                        itemType: data.itemType,
                        referId :data.ruleId,
                        referName: data.itemName,
                        positionTop: event.pageY - data.offsetY,
                        positionLeft: event.pageX - data.offsetX,
                    };
                 vm.ruleDef.nodeList.push(newNode);

                let oldLine = vm.ruleDef.lineList.find(it => it.id === $fu.lineModelId(event.srcElement.id.split('-')[2]));
                let targetNode = vm.ruleDef.nodeList.find(it=>it.id === oldLine.targetId);

                removeLineInView(oldLine);

                let lineData2List = createNewLines(newNode, targetNode.id);

                vm.$nextTick(function() {
                    vm.present = newNode;
                    
                    oldLine.targetId = newNode.id;
                    delete oldLine.deletedByCode;
                    vm.ruleDef.lineList.push(oldLine);
                    drawLine(oldLine);
                    
                    lineData2List.forEach(it=>drawLine(it));
                })
            }
           
            function createNewLines(fromNode,targetId)
            {
                let newLineList = [];
           
                if(fromNode.itemType==='switch' && fromNode.referId){
                    $pinfo.rulePathMap[fromNode.referId].list.forEach(
                        option=>newLineList.push({
                        id: ++ vm.ruleDef.rmaxid,
                        sourceId: fromNode.id,
                        targetId: targetId,
                        referId: option.id,
                        referName: option.code,
                    }));
                    
                }else{
                    newLineList.push({
                        id: ++ vm.ruleDef.rmaxid,
                        sourceId: fromNode.id,
                        targetId: targetId,
                    });
                };
                
                newLineList.forEach(it => vm.ruleDef.lineList.push(it));
                
                return newLineList;
            }
        </script>
    </body>
</html>
